package com.shiwaixiangcun.authz.web.controller;

import com.shiwaixiangcun.authz.AuthzConstants;
import com.shiwaixiangcun.authz.oauth.OAuthAuthxRequest;
import com.shiwaixiangcun.authz.oauth.authorize.CodeAuthorizeHandler;
import com.shiwaixiangcun.authz.service.ClientDetailsService;
import com.shiwaixiangcun.authz.service.OauthService;
import com.shiwaixiangcun.core.CoreConstants;
import com.shiwaixiangcun.core.domain.oauth.ClientDetails;
import com.shiwaixiangcun.core.shiro.AuthzUtils;
import com.shiwaixiangcun.core.repository.search.SearchOperator;
import com.shiwaixiangcun.core.repository.search.Searchable;
import com.shiwaixiangcun.utils.MonkeyUtils;
import com.shiwaixiangcun.utils.SpringUtils;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.net.URLDecoder;

/**
 * URL: oauth2/authorize
 *
 * @author SilentWu
 */
@Controller
@RequestMapping("oauth2/")
public class Oauth2AuthorizeController {

    private static final Logger LOG = LoggerFactory.getLogger(Oauth2AuthorizeController.class);

    @Autowired
    private ClientDetailsService clientDetailsService;

    /**
     * Must handle the grant_type as follow:
     * grant_type="authorization_code" -> response_type="code"
     * ?response_type=code&scope=read,write&client_id=[client_id]&redirect_uri=[redirect_uri]&state=[state]
     * <p>
     * grant_type="implicit"   -> response_type="token"
     * ?response_type=token&scope=read,write&client_id=[client_id]&client_secret=[client_secret]&redirect_uri=[redirect_uri]
     * <p>
     *
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     */
    @RequestMapping("authorize")
    public void authorize(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String code = request.getParameter("code");
        String clientId = request.getParameter("client_id");
        HttpSession session = request.getSession();
        String codeKey = (String) session.getAttribute("codeKey");
        if (MonkeyUtils.isNotEmpty(code) && MonkeyUtils.isNotEmpty(codeKey)) {
            String lowerCode = codeKey.toLowerCase();
            if ((!code.equals(codeKey)) && (!code.equals(lowerCode))) {
                request.setAttribute("third_error", URLDecoder.decode("验证码输入错误", CoreConstants.ENCODING));
                if (MonkeyUtils.isNotEmpty(clientId)) {
                    OauthService oauthService = SpringUtils.getBean(OauthService.class);
                    ClientDetails clientDetails = oauthService.loadClientDetails(clientId);
                    request.setAttribute("clientDetail", clientDetails);
                }
                request.getRequestDispatcher(AuthzConstants.OAUTH_LOGIN_VIEW).forward(request, response);
            }
        }
        try {
            OAuthAuthxRequest oauthRequest = new OAuthAuthxRequest(request);

            if (oauthRequest.isCode()) {
                CodeAuthorizeHandler codeAuthorizeHandler = new CodeAuthorizeHandler(oauthRequest, response);
                LOG.debug("Go to  response_type = 'code' handler: {}", codeAuthorizeHandler);
                codeAuthorizeHandler.handle();
                return;
            }
            unsupportResponseType(oauthRequest, response);
        } catch (OAuthProblemException e) {
            OAuthResponse oAuthResponse = OAuthASResponse
                    .errorResponse(HttpServletResponse.SC_FOUND)
                    .location(e.getRedirectUri())
                    .error(e)
                    .buildJSONMessage();
            AuthzUtils.writeOAuthJsonResponse(response, oAuthResponse);
        }
    }

    private void unsupportResponseType(OAuthAuthxRequest oauthRequest, HttpServletResponse response) throws OAuthSystemException {
        final String responseType = oauthRequest.getResponseType();
        LOG.debug("Unsupport response_type '{}' by client_id '{}'", responseType, oauthRequest.getClientId());

        OAuthResponse oAuthResponse = OAuthResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                .setError(OAuthError.CodeResponse.UNSUPPORTED_RESPONSE_TYPE)
                .setErrorDescription("Unsupport response_type '" + responseType + "'")
                .buildJSONMessage();
        AuthzUtils.writeOAuthJsonResponse(response, oAuthResponse);
    }

    @RequestMapping(value = "platform")
    public String platform(Model model) {
        Searchable searchable = Searchable.newSearchable();
        searchable.addSearchFilter("show", SearchOperator.eq, true);
        model.addAttribute("clients", clientDetailsService.findAll(searchable).getContent());
        return "platform";
    }

    @RequestMapping(value = "login")
    public String oauthLogin(Model model) {
        Searchable searchable = Searchable.newSearchable();
        searchable.addSearchFilter("show", SearchOperator.eq, true);
        model.addAttribute("clients", clientDetailsService.findAll(searchable).getContent());
        return "login";
    }


    @ResponseBody
    @RequestMapping(value = "unauthorized")
    public void unauthorized(HttpServletResponse response) {
        OAuthResponse oAuthResponse;
        try {
            oAuthResponse = OAuthASResponse.errorResponse(403)
                    .setError(OAuthError.ResourceResponse.INVALID_TOKEN)
                    .setErrorDescription("can not access unauthorized resource")
                    .buildJSONMessage();
        } catch (OAuthSystemException e) {
            throw new IllegalStateException(e);
        }

        AuthzUtils.writeOAuthJsonResponse(response, oAuthResponse);
    }

}